/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import com.bourre.data.libs.ConfigLoader;
import com.bourre.data.libs.ConfigLoaderEvent;
import com.bourre.data.libs.XMLToObject;
import com.bourre.data.libs.XMLToObjectDeserializer;
import com.bourre.events.StringEvent;

import fever.app.local.LocalisationEvent;
import fever.conf.BasicConfigurationUI;
import fever.conf.ConfDebug;
import fever.conf.ConfigurationUI;
import fever.conf.FeverConfiguration;
import fever.conf.ParserQueue;
import fever.conf.ParserQueueListener;
import fever.conf.parsers.BitmapParser;
import fever.conf.parsers.ConfigurationParser;
import fever.conf.parsers.DLLParser;
import fever.conf.parsers.FontParser;
import fever.conf.parsers.LocalisationParser;
import fever.conf.parsers.SoundParser;
import fever.conf.parsers.ThemeParser;
import fever.Fever;
import fever.remoting.FeverServiceLocator;
import fever.utils.Stringifier;
import fever.utils.StringUtil;

/**
 * {@code Configuration } class parse xml configuration file used by 
 * {@link fever.core.CoreApplication} class.
 * 
 * <p>Sets the {@link #LISTEN_LOADING_PROGRESS} property to {@code true} before 
 * parsing configuration if you want to display parsing progression.
 * 
 * @author Romain Ecarnot
 */
class fever.conf.Configuration implements ParserQueueListener
{
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var _instance : Configuration;
	private static var _ui : ConfigurationUI;
	
	private var _loader : ConfigLoader;
	private var _parser : ParserQueue;
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/** 
	 * Listen and display progression of loading libraries during configuration.<br />
	 * Default is {@code false}
	 */
	public static var LISTEN_LOADING_PROGRESS : Boolean = false;
	
		
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Returns singleton {@link Configuration} instance.
	 */
	public static function getInstance() : Configuration
	{
		if( !_instance ) _instance = new Configuration();
		return _instance;
	}
	
	/**
	 * Sets the UI for configuration process.
	 */
	public static function setConfigUIListener( ui : ConfigurationUI ) : Void
	{
		_ui = ui;
	}
	
	/**
	 * Returns the UI used by configuration process.
	 */
	public static function getConfigUIListener() : ConfigurationUI
	{
		return _ui;
	}
	
	/**
	 * Adds parsing process for specific configuration object content.
	 * 
	 * <p>If parsing is already running, do nothing.
	 */
	public function addParser( parser : ConfigurationParser ) : Void
	{
		_parser.addType( parser );	
	}
	
	/**
	 * Adds parsing process for specific configuration object content.
	 * 
	 * <p>If parsing is already running, do nothing.
	 */
	public function removeParser( parser : ConfigurationParser ) : Void
	{
		_parser.removeType( parser );
	}
	
	/**
	 * Adds deserializer process type.
	 */
	public function addDeserializationType( type : String, scopeMethod, parsingMethod : Function ) : Void
	{
		_getConfigDeserializer().addType( type, scopeMethod, parsingMethod );	
	}
	
	/**
	 * Removes deserializer process type.
	 */
	public function removeDeserializationType( type : String ) : Void
	{
		_getConfigDeserializer().removeType( type );
	}
	
	/**
	 * Returns {@link fever.conf.FeverConfiguration} object
	 */
	public function getConfig() : FeverConfiguration
	{
		return FeverConfiguration( _loader.getConfig() );
	}
	
	/**
	 * Loads configuration file.
	 * 
	 * <p>file path is defined in {@link fever.Fever#configURL} 
	 * property
	 */
	public function load() : Void
	{
		_loader.addEventListener( ConfigLoader.onLoadInitEVENT, this, _onConfigFileLoaded );
 		_loader.addEventListener( ConfigLoader.onTimeOutEVENT, this, _onError );
 		XMLToObject( _loader.getContent() ).addEventListener( XMLToObject.onErrorEVENT, this, _onError );
 		
 		if( !_ui ) _ui = new BasicConfigurationUI();
 		_dispatchConfigState( 'loading configuration' );
 		
  		_loader.load( Fever.configURL );
	}
	
	/**
	 * A {@link fever.conf.parsers.ConfigurationParser} starts processing.
	 */
	public function onParsingStart( event : StringEvent ) : Void
	{
		_dispatchConfigState( event.getString() );
	}
	
	/**
	 * {@link fever.conf.ParserQueue} dequeue a new 
	 * {@link fever.conf.parsers.ConfigurationParser} instance.
	 */
	public function onParsingChange( event : StringEvent ) : Void
	{
		_dispatchConfigState( event.getString() );
	}
	
	/**
	 * {@link fever.conf.parsers.ConfigurationParser} process complete.
	 */
	public function onParsingComplete( event : StringEvent ) : Void
	{
		_dispatchConfigState( event.getString() );
		
		_onConfigReady();
	}
	
	/**
	 * Triggered during LibParser progression. 
	 */
	public function onParsingProgress( event : StringEvent ) : Void
	{
		if( LISTEN_LOADING_PROGRESS ) _dispatchConfigState( event.getString() );
	}
	
	/**
	 * Returns string representation of instance
	 * 
	 * @return {@code String}
	 */
	public function toString() : String
	{
		return Stringifier.parse( this );	
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	/**
	 * Constructs a new {@code Configuration} instance.
	 * 
	 * <p>Uses {@code Pixlib's ConfigLoader} to load xml structure.
	 */
	private function Configuration( )
	{ 
		ConfigLoader.INIT_TIME_DELAY = 500;
		
		XMLToObjectDeserializer.DESERIALIZE_ATTRIBUTES = true;				
		_loader = new ConfigLoader(  FeverConfiguration.getInstance() );
		_loader.setAntiCache( true );
 		
		_parser = new ParserQueue();
		_parser.addListener( this );
		
		_parser.addType( new LocalisationParser() );
		_parser.addType( new DLLParser() );		_parser.addType( new FontParser() );
		_parser.addType( new ThemeParser() );		_parser.addType( new BitmapParser() );		_parser.addType( new SoundParser() );
	}
	
	/**
	 * Returns current ConfigLoader deserializer.
	 */
	private function _getConfigDeserializer() : XMLToObjectDeserializer
	{
		return XMLToObjectDeserializer( XMLToObject( _loader.getContent() ).getDeserializer() );
	}
	
	/**
	 * XML Configuration file is now loaded, starts parsing.
	 * 
	 * <p>Look at localisation definition.
	 * 
	 * @param event {@code ConfigLoaderEvent} instance
	 */
	private function _onConfigFileLoaded( event : ConfigLoaderEvent ) : Void
	{
		// Fever built-in paths
		getConfig().resourceURL = _trimUrlPath( getConfig().paths.resourceURL.value );
		getConfig().localeURL = _trimUrlPath( getConfig().paths.localeURL.value );
		getConfig().fontURL = _trimUrlPath( getConfig().paths.fontURL.value );		getConfig().dllURL = _trimUrlPath( getConfig().paths.dllURL.value );		getConfig().themeURL = _trimUrlPath( getConfig().paths.themeURL.value );		getConfig().bitmapURL = _trimUrlPath( getConfig().paths.bitmapURL.value );		getConfig().soundURL = _trimUrlPath( getConfig().paths.soundURL.value );
		getConfig().remotingURL = StringUtil.trim( getConfig().paths.remotingURL.value );
		
		// Fever remoting gateway
		if( getConfig().remotingURL != undefined && getConfig().remotingURL.length > 0 )
			FeverServiceLocator.getInstance().init( getConfig().remotingURL );
		
		_parser.execute();
	}
	
	private function _onConfigReady( event : LocalisationEvent ) : Void
	{
		_dispatchConfigState( 'Configuration ok' );
		
		_ui.release();
		
		_global['setTimeout']( this, '_runApplication', 50 );
	}
	
	private function _onError( event : ConfigLoaderEvent ) : Void
	{
		ConfDebug.ERROR( 'Configuration error' );
		
		getConfig().invalidate();
		_runApplication();
	}
	
	private function _runApplication() : Void
	{
		_releaseEventListeners();
		
		Fever.application.start();
	}
	
	private function _trimUrlPath( s : String ) : String
	{
		var r : String = StringUtil.finishWithChar( s, '/', true );
		r = StringUtil.leftTrim( s, StringUtil.EMPTY_CHARS + '/' );
		return ( r ) ? ( r + '/' ) : '';
	}
	
	private function _releaseEventListeners() : Void
	{
		XMLToObject( _loader.getContent() ).release();
		XMLToObject( _loader.getContent() ).removeEventListener( XMLToObject.onErrorEVENT, this );
		
		_loader.release();
		_loader.removeEventListener( ConfigLoader.onTimeOutEVENT, this );
		_loader.removeEventListener( ConfigLoader.onLoadInitEVENT, this );
		
		_parser.removeListener( this );
	}
	
	private function _dispatchConfigState( s : String ) : Void
	{
		_ui.setState( s );	
	}
}